package com.basker.pisces.core.query.criteria.iterate;

import java.util.List;
import java.util.Stack;

import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.basker.pisces.core.query.ICriteria;
import com.basker.pisces.core.query.criteria.Criterion;
import com.basker.pisces.core.query.meta.FieldDesc;
import com.basker.pisces.core.query.meta.QueryMeta;
import com.basker.pisces.core.query.meta.TableDesc;

public class CriteriaIterator {
    private QueryMeta queryMeta;

    public CriteriaIterator(QueryMeta queryMeta) {
        this.queryMeta = queryMeta;
    }

    public void iterate(ICriteria criteria, ICriteriaIterateHandler handler) {
        if (criteria == null || handler == null) {
            return;
        }

        this.iterate(criteria.getCriteria(), new CriteriaIterateContext().merge(criteria), handler);
    }

    public void iterate(List<Criterion> criteria, ICriteriaIterateHandler handler) {
        if (CollectionUtils.isEmpty(criteria) || handler == null) {
            return;
        }

        this.iterate(criteria, new CriteriaIterateContext(), handler);
    }

    private void iterate(ICriteriaIterateHandler handler, Criterion criterion, CriterionIterateArgs args,
                         CriteriaIterateContext context) {
        handler.beginIterate(args);

        if (criterion instanceof ICriteria) {
            ICriteria criteria = (ICriteria) criterion;
            context.merge(criteria);

            this.iterate(criteria.getCriteria(), context, handler);

            context.pop();
        }

        handler.endIterate(args);
    }

    private void iterate(List<Criterion> criteria, CriteriaIterateContext context, ICriteriaIterateHandler handler) {
        int index = 0;

        for (Criterion criterion : criteria) {
            boolean isLast = index == criteria.size() - 1;
            TableDesc fieldDescHostTable = null;

            String fieldId = criterion.getFieldId();
            if (!StringUtils.isEmpty(fieldId)) {
                FieldDesc fieldDesc = this.queryMeta.getField(fieldId);
                fieldDescHostTable = fieldDesc.getHostTable();
            }

            CriterionIterateArgs args = new CriterionIterateArgs(queryMeta, criterion, fieldDescHostTable, isLast);

            iterate(handler, criterion, args, context);
            index++;
        }

    }

    static class CriteriaIterateContext {
        private Stack<ICriteria> criteriaStack = new Stack<>();

        public CriteriaIterateContext merge(ICriteria criteria) {
            criteriaStack.push(criteria);
            return this;
        }

        public void pop() {
            criteriaStack.pop();
        }
    }
}
